package com.yunji.order.event.listener;

import com.yunji.base.bo.BelongAmountModifyBO;
import com.yunji.base.enums.ScaleBelongTypeEnum;
import com.yunji.base.service.BelongService;
import com.yunji.common.enums.business.TradeMode;
import com.yunji.common.enums.business.TradeTypes;
import com.yunji.common.exception.ServiceException;
import com.yunji.common.utils.bean.BeanUtils;
import com.yunji.coupon.bo.UseCouponBO;
import com.yunji.coupon.doamin.CouponOrderDO;
import com.yunji.coupon.dto.OrderScaleDTO;
import com.yunji.order.consumer.OrderConsumer;
import com.yunji.order.context.convert.OrderConvert;
import com.yunji.order.context.convert.TradeConvert;
import com.yunji.order.domain.OrderDO;
import com.yunji.order.domain.OrderScaleDO;
import com.yunji.order.domain.RefundDO;
import com.yunji.order.domain.TradeDO;
import com.yunji.order.bo.OrderScaleBO;
import com.yunji.order.event.OrderCouponEvent;
import com.yunji.order.event.OrderRefundScaleEvent;
import com.yunji.order.event.OrderScaleEvent;
import com.yunji.order.service.OrderScaleService;
import com.yunji.order.service.OrderService;
import com.yunji.order.service.TradeService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

/**
 * @author : peter-zhu
 * @date : 2025/3/20 17:49
 * @description : TODO
 **/
@Slf4j
@Component
public class OrderAndRefundListener {
    @Resource
    protected OrderService orderService;

    @Resource
    protected BelongService belongService;

    @Resource
    protected TradeService tradeService;

    @Resource
    protected OrderScaleService scaleService;

    @Resource
    protected OrderConsumer orderConsumer;

    @Transactional(rollbackFor = Exception.class)
    @EventListener
    public void handle(OrderCouponEvent event) {
        OrderDO order = event.getOrderDO();
        if (event.getCouponId() != null) {
            CouponOrderDO coDomain = new CouponOrderDO();
            coDomain.setOrderId(order.getOrderId());
            coDomain.setOrderCode(order.getOrderCode());
            coDomain.setCouponId(event.getCouponId());
            boolean rs = orderConsumer.userCoupon(new UseCouponBO(coDomain, null, order.getCouponAmount()));
            if (!rs) {
                log.error("OrderBusiness create order use coupon fail ,CouponId:{}", event.getCouponId());
                throw new ServiceException("优惠券已被使用");
            }
        }
    }

    private static TradeDO buildDefault(OrderScaleEvent event, OrderDO orderDO) {
        TradeDO trade = new TradeDO();
        trade.setTransactionNo(orderDO.getOrderCode());
        trade.setTradeType(TradeTypes.PRODUCT_BUYING.getCode());
        trade.setTradeMode(TradeMode.ORDER.getCode());
        trade.setOpenId(orderDO.getOpenId());
        trade.setApiTime(event.getPayTime());
        trade.setApiCode(event.getPayCode());
        trade.setTradeAmount(orderDO.getActualAmount());
        return trade;
    }

    @Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
    @EventListener
    public void handle(OrderScaleEvent event) {
        OrderDO orderDO = event.getOrderDO();
        // 三个分润记录 平台分润 代理商分润 网点分润
        List<OrderScaleDO> scaleDetailDOList = Lists.newArrayList();
        // 四个交易 订单交易单 平台分润交易 代理商分润交易 网点分润交易
        List<TradeDO> tradeDOList = Lists.newArrayList();
        //  构建订单交易单
        TradeDO trade = buildDefault(event, orderDO);
        tradeDOList.add(trade);
        Map<ScaleBelongTypeEnum, OrderScaleDTO> scaleMap = event.getScaleMap();
        Collection<OrderScaleDTO> values = scaleMap.values();
        for (OrderScaleDTO sc : values) {
            TradeDO newTrade = TradeConvert.convertTrade(sc, trade);
            tradeDOList.add(newTrade);
        }
        tradeService.createTrades(tradeDOList);
        for (OrderScaleDTO sc : values) {
            OrderScaleDO scale = OrderConvert.convertScaleDetail(sc);
            scale.setOrderId(orderDO.getOrderId());
            scale.setOrderCode(orderDO.getOrderCode());
            scale.setDataCode(orderDO.getOrderCode());
            scaleDetailDOList.add(scale);
        }
        scaleService.createScales(scaleDetailDOList);
        OrderScaleDTO siteScale = scaleMap.get(ScaleBelongTypeEnum.SITE);
        belongService.updateAccount(BelongAmountModifyBO.builder().belongId(siteScale.getAccountId())
                .frozenAmount(siteScale.getAmt()).earnAmount(siteScale.getAmt()).build());
        OrderScaleDTO agentScale = scaleMap.get(ScaleBelongTypeEnum.AGENT);
        belongService.updateAccount(BelongAmountModifyBO.builder().belongId(agentScale.getAccountId())
                .frozenAmount(agentScale.getAmt()).earnAmount(agentScale.getAmt()).build());
        // 记录成功分润
        OrderDO updateOrder = new OrderDO();
        updateOrder.setOrderId(orderDO.getOrderId());
        updateOrder.setFinishTime(new Date());
        updateOrder.setScale(1);
        orderService.updateOrder(updateOrder);
    }

    @Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
    @EventListener
    public void handle(OrderRefundScaleEvent event) {
        log.info("开始退款分润{}", event);
        RefundDO refundDO = event.getRefundDO();
        OrderDO order = orderService.getOrderById(refundDO.getOrderId());
        List<OrderScaleDO> orderScaleDetails = scaleService.getOrderScaleList(OrderScaleBO.builder().
                dataCode(refundDO.getOrderCode()).build());
        BigDecimal refundRatio = refundDO.getRefundRatio();
        // 四个交易退款 订单交易退款单 平台分润交易退款 代理商分润交易退款 网点分润交易退款
        List<TradeDO> refundTradeDetails = Lists.newArrayList();
        List<TradeDO> tradeDOS = tradeService.listByTranNo(refundDO.getOrderCode());
        for (TradeDO trd : tradeDOS) {
            TradeDO refundTradeDO = new TradeDO();
            BeanUtils.clone(trd, refundTradeDO);
            refundTradeDO.setTradeId(null);//克隆后设置为NULL 因为是自增id的新增数据
            refundTradeDO.setTransactionNo(refundDO.getRefundCode());
            refundTradeDO.setTradeType(Optional.ofNullable(TradeTypes.get(trd.getTradeType())).map(TradeTypes::getRefundCode).orElse(-1));
            refundTradeDO.setTradeMode(TradeMode.REFUND.getCode());
            refundTradeDO.setApiTime(event.getRefundTime());
            refundTradeDO.setApiCode(event.getRefundCode());
            refundTradeDO.setTradeAmount(trd.getTradeAmount().multiply(refundRatio).setScale(2, RoundingMode.DOWN).negate());
            refundTradeDO.setPayType(0);
            refundTradeDO.setCreateTime(new Date());
            refundTradeDetails.add(refundTradeDO);
        }
        tradeService.createTrades(refundTradeDetails);
        // 三个分润退款记录 平台退款分润 代理商退款分润 网点退款分润
        List<OrderScaleDO> refundScaleDetails = Lists.newArrayList();
        for (OrderScaleDO osc : orderScaleDetails) {
            OrderScaleDO refundScaleDO = new OrderScaleDO();
            BeanUtils.clone(osc, refundScaleDO);
            refundScaleDO.setScaleId(null);//克隆后设置为NULL 因为是自增id的新增数据
            refundScaleDO.setOrderId(refundDO.getOrderId());
            refundScaleDO.setOrderCode(refundDO.getOrderCode());
            refundScaleDO.setDataCode(refundDO.getRefundCode());
            refundScaleDO.setTradeType(Optional.ofNullable(TradeTypes.get(osc.getTradeType())).map(TradeTypes::getRefundCode).orElse(-1));
            refundScaleDO.setCreateTime(new Date());
            BigDecimal scaleRefundAmt = osc.getScaleAmount().multiply(refundRatio).setScale(2, RoundingMode.DOWN);
            refundScaleDO.setScaleAmount(scaleRefundAmt.negate());
            refundScaleDetails.add(refundScaleDO);
            if (order.getFrozen() == 1)
                //如果解冻从余额扣款
                belongService.updateAccount(BelongAmountModifyBO.builder().belongId(osc.getAccountId())
                        .refundAmount(scaleRefundAmt).remainAmount(scaleRefundAmt.negate()).build());
            else
                //如果还没解冻从解冻金额扣款
                belongService.updateAccount(BelongAmountModifyBO.builder().belongId(osc.getAccountId())
                        .refundAmount(scaleRefundAmt).frozenAmount(scaleRefundAmt.negate()).build());
        }
        scaleService.createScales(refundScaleDetails);
        log.info("结束退款分润{}", event);
    }
}
